using Cysharp.Threading.Tasks;
using System.Collections.Generic;
using System;
using MoonFramework.Scripts.Resoure;
using UnityEngine;
using Object = UnityEngine.Object;


namespace MoonFramework.Template
{
	/// <summary>
	/// 缓存池树节点
	/// </summary>
	public class PoolUnityObjNode
	{
		public GameObject fatherObj;
		public Stack<UnityEngine.Object> poolStack;

		public PoolUnityObjNode(string name, GameObject poolObj = null)
		{
			fatherObj = new GameObject(name);
			if (poolObj)
			{
				fatherObj.transform.parent = poolObj.transform;
			}
			poolStack = new Stack<UnityEngine.Object>();
		}

		public void Push(UnityEngine.Object obj)
		{
			poolStack.Push(obj);

			if (obj is GameObject go)
			{
				go.transform.parent = fatherObj.transform;
				go.SetActive(false);
			}
		}

		public UnityEngine.Object Pop()
		{
			var obj = poolStack.Pop();

			if (obj is GameObject go)
			{
				go.SetActive(true);
				go.transform.parent = null;
			}

			return obj;
		}

		public int Count => poolStack.Count;
	}

	/// <summary>
	/// 缓存池管理器
	/// </summary>
	public class GameObjPoolManager : BaseMonoManager<GameObjPoolManager>
	{
		private readonly Dictionary<string, PoolUnityObjNode> pool = new();

		[SerializeField] private GameObject PoolObj;

		/// <summary>
		/// 缓存池
		/// </summary>
		/// <param name="name">对象名称</param>
		/// <param name="callback">获取对象后的回调</param>
		public async UniTask<T> PopAsync<T>(string name, Action<UnityEngine.Object> callback = null)where T : Object
		{
			T obj;
			if (pool.TryGetValue(name, out var poolNode) && poolNode.Count > 0)
			{
				obj = poolNode.Pop() as T;
				callback?.Invoke(obj);
			}
			else
				obj = await ResourceManager.Instance.LoadAsync<T>(name);
			obj.name = name;
			callback?.Invoke(obj);
			return obj;
		}
		
		public T Pop<T>(string name,GameObject prefab,  Action<UnityEngine.Object> callback = null)where T : Object
		{
			T obj;
			if (pool.TryGetValue(name, out var poolNode) && poolNode.Count > 0)
			{
				obj = poolNode.Pop() as T;
				callback?.Invoke(obj);
			}
			else
				obj = Instantiate(prefab) as T;
			obj.name = name;
			callback?.Invoke(obj);
			return obj;
		}

		/// <summary>
		/// Push节点
		/// </summary>
		/// <param name="name">对象名称</param>
		/// <param name="obj">对象</param>
		public void Push(string name, UnityEngine.Object obj)
		{
			if (!pool.TryGetValue(name, out var poolNode))
			{
				poolNode = new PoolUnityObjNode(name, PoolObj);
				pool[name] = poolNode;
			}
			poolNode.Push(obj);
		}

		/// <summary>
		/// 预存
		/// </summary>
		/// <param name="name">对象名称</param>
		/// <param name="obj">对象</param>
		/// <param name="count">预加载数量</param>
		public async UniTask Preload(string name, UnityEngine.Object obj, int count)
		{
			if (!pool.TryGetValue(name, out var poolNode))
			{
				poolNode = new PoolUnityObjNode(name, PoolObj);
				pool[name] = poolNode;
			}

			for (int i = 0; i < count; i++)
			{
				// 异步加载资源
				await ResourceManager.Instance.LoadAsync<UnityEngine.Object>(name, res =>
				{
					res.name = name;
					poolNode.Push(res);
				});
			}
		}

		/// <summary>
		/// 清空内存池，切换场景时生效
		/// </summary>
		public void Clear()
		{
			foreach (var poolNode in pool.Values)
			{
				if (poolNode.fatherObj != null)
				{
					Destroy(poolNode.fatherObj);
				}

				while (poolNode.Count > 0)
				{
					var obj = poolNode.Pop();
					if (obj is GameObject gameObject)
					{
						Destroy(gameObject);
					}
				}
			}
			pool.Clear();
			Destroy(PoolObj);
		}

		public void RemoveType(string name)
		{
			if (pool.ContainsKey(name))
				pool.Remove(name);
		}
	}
}